// RPC stubs for clients to talk to extent_server

#include "extent_client.h"
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

// The calls assume that the caller holds a lock on the extent

extent_client::extent_client(std::string dst)
{
  sockaddr_in dstsock;
	make_sockaddr(dst.c_str(), &dstsock);
  cl = new rpcc(dstsock);
  if (cl->bind() != 0) {
    printf("extent_client: bind failed\n");
  }
}

extent_protocol::status
extent_client::get(extent_protocol::extentid_t eid, std::string &buf)
{
  printf("extent client get id: %llu\n", eid);
  std::map<extent_protocol::extentid_t, data>::iterator it;
  extent_protocol::status ret = extent_protocol::OK;
  it = extent_cache.find(eid);

  // found in cache and not marked for deletion
  if(it != extent_cache.end() && (it->second).deleted == 0){
    buf = (it->second).contents;
  }
  // marked for deletion
  else if((it->second).deleted == 1){
    return extent_protocol::NOENT;
  }
  // not marked for deletion
  else{
    // put into cache
    extent_protocol::attr at;
    data d;
    ret = cl->call(extent_protocol::get, eid, buf);
    cl->call(extent_protocol::getattr, eid, at);
    d.contents = buf;
    d.modified = 0;
    d.deleted = 0;
    d.a = at;
    extent_cache[eid] = d;
  }
    return ret;
}

extent_protocol::status
extent_client::getattr(extent_protocol::extentid_t eid, 
		       extent_protocol::attr &attr)
{
  std::map<extent_protocol::extentid_t, data>::iterator it;
  it = extent_cache.find(eid);
  // found in cache
  if(it != extent_cache.end()){
    attr = (it->second).a;
    return extent_protocol::OK;
  }
  // not found in cache
  else{
    extent_protocol::status ret = extent_protocol::OK;
    ret = cl->call(extent_protocol::getattr, eid, attr);
    return ret;
  }
}

extent_protocol::status
extent_client::put(extent_protocol::extentid_t eid, std::string buf)
{
  // set data to hold item to be cached

  data d;
  time_t now = time(NULL);
  extent_protocol::attr atr;
  extent_protocol::status ret = extent_protocol::OK;
  atr.size = buf.length();
  atr.atime = now;
  atr.ctime = now;
  atr.mtime = now;
  d.contents = buf;
  d.modified = 1;
  d.deleted = 0;
  d.a = atr;

  // already found in cache so replace
  if(extent_cache.find(eid) != extent_cache.end()){
    extent_cache.erase(eid);
    extent_cache[eid] = d;
  } 
  // not found in cache so put in extent server and cache
  else{ 
    extent_cache[eid] = d;
    int r;
    ret = cl->call(extent_protocol::put, eid, buf, r);
  }
  return ret;
}

extent_protocol::status
extent_client::remove(extent_protocol::extentid_t eid)
{
  // mark item for removal from cache
  extent_protocol::status ret = extent_protocol::OK;
  std::map<extent_protocol::extentid_t, data>::iterator it;
  it = extent_cache.find(eid);
  if(it != extent_cache.end()){
    extent_cache.erase(eid);
    data d;
    d.contents = "";
    d.modified = 1;
    d.deleted = 1;
    extent_cache[eid] = d;
  }
  return ret;
}

extent_protocol::status 
extent_client::flush(extent_protocol::extentid_t eid)
{
  std::map<extent_protocol::extentid_t, data>::iterator it;
  extent_protocol::status ret = extent_protocol::OK;
  it = extent_cache.find(eid);

  // found in cache
  if(it != extent_cache.end() && (it->second).deleted == 0){
    int modify = (it->second).modified;
    std::string buf  = (it->second).contents;

    // file was not modified (not dirty)
    if(modify == 0){
      extent_cache.erase(eid);
      return ret;
    }

    // file has been modified (dirty)
    else{
      int r;
      ret = cl->call(extent_protocol::put, eid, buf, r);
      extent_cache.erase(eid);
      return ret;
    }
  }

  // marked for deletion so delete it from cache and server
  else if(it != extent_cache.end() && (it->second).deleted == 1){
    std::string c = (it->second).contents;
    int m = (it->second).modified;
    int d = (it->second).deleted;
    printf("contents: %s, modify: %d, delete: %d\n", c.c_str(), m, d);
    printf("delete bit is set to 1\n");
    extent_cache.erase(eid);
    int r;
    if(cl->call(extent_protocol::get, eid, r) == extent_protocol::OK){
      int p;
      ret = cl->call(extent_protocol::remove, eid, p);
    }
    return ret;
  }
  // wasn't found in cache
  else {
    return extent_protocol::NOENT;
  }
}
